/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * rulePanel.java
 *
 * Created on 14.05.2009, 20:08:30
 */
package peripheral.designer.wizard;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import peripheral.designer.property.PropertyPanel;
import peripheral.logic.Logging;
import peripheral.logic.rule.Condition;
import peripheral.logic.rule.ConditionOperation;
import peripheral.logic.rule.DefaultRule;
import peripheral.logic.rule.Rule;
import peripheral.logic.sensor.Sensor;
import peripheral.logic.sensor.SensorChannel;
import peripheral.logic.symboladapter.SymbolAdapter;
import peripheral.logic.value.SensorValue;

/**
 *
 * @author Berni
 */
public class RulePanel extends javax.swing.JPanel {

    private Container parent;
    private Rule rule;
    private List<Condition> conditions;
    private SymbolAdapter adapter;
    private GridBagLayout layout;
    private JPanel buttonPanel;
    private List<SensorChannel> availableSensorChannels;
    private List<JComboBox> sensorValues;
    private List<JComboBox> operations;
    private List<Component> rightSideComponents;
    private PropertyPanel actionPropertyPanel;

    /** Creates new form rulePanel */
    public RulePanel(Rule rule, Container parent, int index) {
        initComponents();

        this.parent = parent;

        layout = new GridBagLayout();
        this.setLayout(layout);

        this.rule = rule;
        if (rule.getActions().size() == 0) {
            this.rule.getActions().add(rule.getAdapter().getDefaultAction().clone());
        }/* else {
        this.rule.getActions().set(0, rule.getAdapter().getDefaultAction());
        }*/

        this.conditions = rule.getConditions();

        this.adapter = rule.getAdapter();

        //@todo: call every time the rulepanel is shown up, not only on creation
        updateSensorChannels();

        buttonPanel = createButtonPanel();

        //hold components in a list, so that the relation between condition and
        //components can be established based on the index in the list
        this.sensorValues = new ArrayList<JComboBox>();
        this.operations = new ArrayList<JComboBox>();
        this.rightSideComponents = new ArrayList<Component>();

        createLayout();

        if (rule instanceof DefaultRule) {
        	setBorder(javax.swing.BorderFactory.createTitledBorder("Defaultrule - Behavior if no other rule is valid"));
        }
        else {
        	this.setRuleIndex(index);
        	if (conditions.size() == 0) {

                addNewCondition();
            }
        }
        

    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 483, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 98, Short.MAX_VALUE)
        );
    }// </editor-fold>//GEN-END:initComponents


    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
    private JPanel createButtonPanel() {
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(null);

        JButton addRuleButton = new JButton("+");
        addRuleButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                addNewCondition();
            }
        });
        
        if (rule instanceof DefaultRule) {
        	addRuleButton.setEnabled(false);
        }

        buttonPanel.setSize(90, 25);
        buttonPanel.setPreferredSize(new Dimension(90, 25));
        addRuleButton.setBounds(5, 5, 80, 20);

        buttonPanel.add(addRuleButton);

        return buttonPanel;
    }

    private void updateContent() {

        this.removeAll();

        this.createLayout();

        this.validate();

        parent.validate();
    }

    private void createLayout() {

        int i = 0;

        GridBagConstraints gbc;

        /*this.sensorValues.clear();
        this.operations.clear();
        this.rightSideComponents.clear();*/

        JLabel lblConditions = new JLabel("Conditions:");
        gbc = makegbc(0, 0, 1, 1, GridBagConstraints.BOTH, lblConditions.getWidth(), lblConditions.getHeight());
        layout.setConstraints(lblConditions, gbc);
        this.add(lblConditions);


        for (final Condition condition : conditions) {

            //left side
            JComboBox sensorValue = null;
            if (this.sensorValues.size() <= i) {
                sensorValue = new JComboBox();
                this.sensorValues.add(sensorValue);

                for (SensorChannel channel : this.availableSensorChannels) {
                    sensorValue.addItem(channel);
                }

                sensorValue.setSelectedItem(condition.getLeftSideOp().getSensorChannel());

                sensorValue.addItemListener(
                        new ItemListener() {

                            public void itemStateChanged(ItemEvent e) {
                                //Condition condition = RulePanel.this.sensorValueConditionMapping.get(e.getSource());
                                SensorChannel selectedChannel = (SensorChannel) ((JComboBox) e.getSource()).getSelectedItem();
                                ConditionOperation co = null;
                                int index = conditions.indexOf(condition);

                                if (selectedChannel != SensorChannel.getDummy()) {
                                    SensorValue leftSideOp = condition.getLeftSideOp();
                                    if (leftSideOp == null) {
                                        leftSideOp = new SensorValue(adapter, rule.getPrefix(condition), Object.class);
                                    }
                                    boolean sensorValueTypeChanged = !selectedChannel.getDatatype().equals(leftSideOp.getSensorChannel().getDatatype());
                                    leftSideOp.setSensorChannel(selectedChannel);
                                    condition.setLeftSideOp(leftSideOp);

                                    updateAvailableOperations(condition, operations.get(index), sensorValueTypeChanged);

                                    co = (ConditionOperation) operations.get(index).getSelectedItem();

                                }
                                updateRightSideComponent(co, index);
                                condition.setOperation(co);
                            /*if (co != null) {
                            rightSideComponents.set(index, co.getRightSideComponent());
                            } else {
                            rightSideComponents.set(index, null);
                            }*/
                            }
                        });
            } else {
                sensorValue = this.sensorValues.get(i);
            }

            gbc = makegbc(0, i + 1, 1, 1, GridBagConstraints.BOTH, sensorValue.getPreferredSize().width, sensorValue.getPreferredSize().height);
            layout.setConstraints(sensorValue, gbc);
            this.add(sensorValue);

            //operation
            JComboBox operation = null;
            if (this.operations.size() <= i) {
                operation = new JComboBox();

                this.operations.add(operation);

                operation.addItemListener(
                        new ItemListener() {

                            public void itemStateChanged(ItemEvent e) {
                                if (e.getStateChange() == ItemEvent.SELECTED) {
                                    JComboBox operation = ((JComboBox) e.getSource());
                                    ConditionOperation co = (ConditionOperation) operation.getSelectedItem();
                                    Logging.getLogger().finest(co.toString());
                                    if (co != null) {
                                        updateRightSideComponent(co, operations.indexOf(operation));
                                        condition.setOperation(co);
                                    }
                                //rightSideComponents.set(operations.indexOf(operation), co.getRightSideComponent());
                                //updateContent();
                                }
                            }
                        });
            } else {
                operation = this.operations.get(i);
            }
            updateAvailableOperations(condition, operation, false);

            gbc = makegbc(1, i + 1, 1, 1, GridBagConstraints.BOTH, (int) operation.getWidth(), (int) operation.getHeight());
            layout.setConstraints(operation, gbc);
            this.add(operation);

            //right side
            /*Component com = new JLabel("Select operation.");
            if (operation.getSelectedItem() != null){
            com = ((ConditionOperation)operation.getSelectedItem()).getRightSideComponent();
            }
            //com = new JLabel("place holder");

            gbc = makegbc(2, i, 1, 1, GridBagConstraints.BOTH, com.getPreferredSize().width, com.getPreferredSize().height);

            layout.setConstraints(com, gbc);

            this.add(com);*/
            this.updateRightSideComponent((ConditionOperation) operation.getSelectedItem(), i);

            //delete button
            JButton delButton = new JButton("delete");
            if (conditions.size() <= 1) {
                delButton.setEnabled(false);
            }

            delButton.addActionListener(new DelActionListener(condition));

            gbc = makegbc(3, i + 1, 1, 1, GridBagConstraints.BOTH, (int) delButton.getWidth(), (int) delButton.getHeight());

            layout.setConstraints(delButton, gbc);

            this.add(delButton);

            i++;
        }

        //add button panel at least
        gbc = makegbc(3, i + 1, 1, 1, GridBagConstraints.NONE, (int) buttonPanel.getWidth(), (int) buttonPanel.getHeight());
        layout.setConstraints(buttonPanel, gbc);
        this.add(buttonPanel);

        JLabel lblActions = new JLabel("Action: " + rule.getAdapter().getDefaultAction().getName());
        gbc = makegbc(0, i + 3, 1, 1, GridBagConstraints.BOTH, lblActions.getWidth(), lblActions.getHeight());
        layout.setConstraints(lblActions, gbc);
        this.add(lblActions);

        if (actionPropertyPanel == null) {
            actionPropertyPanel = new PropertyPanel(rule.getAdapter(), rule.getUserInput());
        }
        gbc = makegbc(0, i + 4, 1, 1, GridBagConstraints.BOTH, actionPropertyPanel.getWidth(), actionPropertyPanel.getHeight());
        layout.setConstraints(actionPropertyPanel, gbc);
        this.add(actionPropertyPanel);
    }

    private void updateSensorChannels() {
        this.availableSensorChannels = new ArrayList<SensorChannel>();

        this.availableSensorChannels.add(SensorChannel.getDummy());

        for (Sensor sensor : adapter.getPreselectedSensors()) {
            this.availableSensorChannels.addAll(sensor.getSensorChannels());
        }
    }

    private void updateAvailableOperations(Condition condition, JComboBox operation, boolean sensorValueTypeChanged) {
        Object selection = operation.getSelectedItem();
        if (selection == null) {
            selection = condition.getOperation();
        }

        List<ConditionOperation> operationsToAdd = new ArrayList<ConditionOperation>();

        if (sensorValueTypeChanged) {
            operationsToAdd = condition.getAvailableOperations();
        } else {
            for (ConditionOperation op : condition.getAvailableOperations()) {
                int existingOperation = -1;
                for (int i = 0; i < operation.getItemCount(); i++) {
                    if (operation.getItemAt(i).equals(op)) {
                        existingOperation = i;
                        break;
                    }
                }
                if (existingOperation != -1) {
                    operationsToAdd.add((ConditionOperation) operation.getItemAt(existingOperation));
                } else {
                    if (op.equals(selection)) {
                        operationsToAdd.add((ConditionOperation)selection);
                    } else {
                        operationsToAdd.add(op);
                    }
                }
            }
        }

        operation.removeAllItems();

        for (ConditionOperation op : operationsToAdd) {
            operation.addItem(op);
        }

        if (selection == null) {
            if (operation.getItemCount() > 0) {
                operation.setSelectedIndex(0);
            }
        } else {
            operation.setSelectedItem(selection);
            //ConditionOperation test = (ConditionOperation) operation.getSelectedItem();
            //test.getRightSideComponent().repaint();
        }


        this.validate();
        this.revalidate();
    //com.repaint();
    }

    private void updateRightSideComponent(ConditionOperation operation, int index) {
        //remove previous rightSideComponent
        if (this.rightSideComponents.size() > index) {
            Component old = this.rightSideComponents.get(index);
            if (old != null) {
                this.remove(old);
            }
        }

        Component com = new JLabel("Select operation.");
        com.setPreferredSize(new Dimension(50, 10));
        if (operation != null) {
            com = operation.getRightSideComponent();
        }

        //add components at first run --> initialization
        if (index >= this.rightSideComponents.size()) {
            this.rightSideComponents.add(com);
        //replace components when operation changes
        } else {
            this.rightSideComponents.set(index, com);
        }

        GridBagConstraints gbc = makegbc2(2, index + 1, 1, 1, GridBagConstraints.BOTH, com.getPreferredSize().width, com.getPreferredSize().height);

        layout.setConstraints(com, gbc);

        this.add(com);

        this.validate();
        this.revalidate();
        com.repaint();
    }

    private void addNewCondition() {

        Condition cond = new Condition();
        cond.setLeftSideOp(new SensorValue(adapter, rule.getPrefix(cond), Object.class));
        conditions.add(cond);
        updateContent();

    }

    private void removeCondition(Condition con) {
        int index = conditions.indexOf(con);

        this.sensorValues.remove(index);
        this.operations.remove(index);
        this.rightSideComponents.remove(index);
        conditions.remove(con);

    //updateContent();

    }

    /**
     * @return created rule including it's conditions and actions by this panel
     */
    public Rule getRule() {
        return rule;
    }
    
    public void setRuleIndex(int index) {
    	setBorder(javax.swing.BorderFactory.createTitledBorder("Rule "+index));
    }

    public Dimension getMinimumDisplaySize() {
        return new Dimension(200, 10);
    }

    /**
     * creates the settings for a component which should be displayed in GridBagLayout
     * @param x startcoordinate of component
     * @param y startcoordinate of component
     * @param width how many cells
     * @param height how many cells
     * @return
     */
    private GridBagConstraints makegbc(int x, int y, int width, int height, int fill, int ipadx, int ipady) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = x;
        gbc.gridy = y;
        gbc.gridwidth = width;
        gbc.gridheight = height;
        gbc.fill = fill;
        //gbc.ipadx = ipadx;
        //gbc.ipady = ipady;
        //Defines the border of each element
        gbc.insets = new Insets(5, 5, 5, 5);

        return gbc;
    }

    private GridBagConstraints makegbc2(int x, int y, int width, int height, int fill, int ipadx, int ipady) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = x;
        gbc.gridy = y;
        gbc.gridwidth = width;
        gbc.gridheight = height;
        gbc.fill = fill;
        gbc.ipadx = ipadx;
        gbc.ipady = ipady;
        //Defines the border of each element
        gbc.insets = new Insets(5, 5, 5, 5);

        return gbc;
    }

    /**
     * Inner class to remember condition a button belongs to
     */
    class DelActionListener implements ActionListener {

        Condition con;

        public DelActionListener(Condition con) {

            this.con = con;
        }

        public void actionPerformed(ActionEvent e) {
            removeCondition(con);

            updateContent();
        }
    }
}
